/* eslint-disable @typescript-eslint/indent */
/**
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { ref, Ref, computed } from 'vue';
import { DataGridColumn, DataGridProps } from '../data-grid.props';
import { DataGridHeaderCell, UseColumn, UseDataView, UseSort } from './types';
import { DataViewSorter } from './use-data-view';

export interface ColumnRenderContext {
    defaultColumnWidth: number;
    headerDepth: number;
    leftColumns: DataGridColumn[];
    leftColumnsMap: Map<string, DataGridColumn>;
    leftColumnsWidth: number;
    leftHeaderColumns: DataGridHeaderCell[];
    primaryColumns: DataGridColumn[];
    primaryColumnsMap: Map<string, DataGridColumn>;
    primaryColumnsWidth: number;
    primaryHeaderColumns: DataGridHeaderCell[];
    rightColumns: DataGridColumn[];
    rightColumnsMap: Map<string, DataGridColumn>;
    rightColumnsWidth: number;
    rightHeaderColumns: DataGridHeaderCell[];
    summaryColumns: DataGridColumn[];
}

export function useColumn(
    props: DataGridProps
): UseColumn {
    const defaultColumnWidth = 120;

    const columnContext: Ref<ColumnRenderContext> = ref({
        defaultColumnWidth,
        headerDepth: 1,
        leftColumns: [],
        leftColumnsMap: new Map<string, DataGridColumn>(),
        leftColumnsWidth: 0,
        leftHeaderColumns: [],
        primaryColumns: [],
        primaryColumnsMap: new Map<string, DataGridColumn>(),
        primaryColumnsWidth: 0,
        primaryHeaderColumns: [],
        rightColumns: [],
        rightColumnsMap: new Map<string, DataGridColumn>(),
        rightColumnsWidth: 0,
        rightHeaderColumns: [],
        summaryColumns: []
    });

    const summaryOptions = ref(props.summary);

    const summaryFields = computed(() => {
        const options = summaryOptions.value;
        return options?.groupFields || [];
    });

    function updateColumnSettingIcon() {
        columnContext.value.rightColumns.forEach((column: DataGridColumn) => { column.showSetting = false; });
        columnContext.value.primaryColumns.forEach((column: DataGridColumn) => { column.showSetting = false; });
        const visibleRightColumns = columnContext.value.rightColumns.filter((column: DataGridColumn) => column.visible);
        const visiblePrimaryColumns = columnContext.value.primaryColumns.filter((column: DataGridColumn) => column.visible);
        if (visibleRightColumns.length > 0) {
            visibleRightColumns[visibleRightColumns.length - 1].showSetting = true;
        } else if (visiblePrimaryColumns.length > 0) {
            visiblePrimaryColumns[visiblePrimaryColumns.length - 1].showSetting = true;
        }
    }

    function updateColumnRenderContext(columns: DataGridColumn[]) {
        const summaryColumnsMap = new Map<string, boolean>();
        if (summaryFields.value) {
            summaryFields.value.reduce((result: Map<string, boolean>, summaryField: string) => {
                result.set(summaryField, true);
                return result;
            }, summaryColumnsMap);
        }
        columns.reduce((result, column) => {
            column.visible = column.visible !== false;
            if (column.fixed === 'left') {
                columnContext.value.leftColumns.push(column);
                columnContext.value.leftColumnsMap.set(column.field, column);
                columnContext.value.leftColumnsWidth += column.actualWidth || defaultColumnWidth;
            } else if (column.fixed === 'right') {
                columnContext.value.rightColumns.push(column);
                columnContext.value.rightColumnsMap.set(column.field, column);
                columnContext.value.rightColumnsWidth += column.actualWidth || defaultColumnWidth;
            } else {
                columnContext.value.primaryColumns.push(column);
                columnContext.value.primaryColumnsMap.set(column.field, column);
                columnContext.value.primaryColumnsWidth += column.actualWidth || defaultColumnWidth;
            }
            if (summaryColumnsMap.has(column.field)) {
                columnContext.value.summaryColumns.push(column);
            }
            return result;
        }, columnContext);
        updateColumnSettingIcon();
        return columnContext;
    }

    updateColumnRenderContext(props.columns);

    const hasLeftFixedColumn = computed(() => {
        return columnContext.value.leftColumns.length > 0;
    });

    const hasRightFixedColumn = computed(() => {
        return columnContext.value.rightColumns.length > 0;
    });

    function sortableReducer(columns: DataGridColumn[], gridColumn: DataGridColumn) {
        if (gridColumn.sortable && gridColumn.sort && gridColumn.sort !== 'none') {
            columns.push(gridColumn);
        }
        return columns;
    }

    function sortOrderCompare(preColumn: DataGridColumn, postColumn: DataGridColumn): number {
        preColumn.sortOrder = preColumn.sortOrder || 0;
        postColumn.sortOrder = postColumn.sortOrder || 0;
        return preColumn.sortOrder === postColumn.sortOrder ? 0 : (preColumn.sortOrder < postColumn.sortOrder ? -1 : 1);
    }

    function decendCompare(preColumn: DataGridColumn, postColumn: DataGridColumn) {
        preColumn.sortOrder = preColumn.sortOrder || 0;
        postColumn.sortOrder = postColumn.sortOrder || 0;
        return preColumn.sortOrder === postColumn.sortOrder ? 0 : (preColumn.sortOrder < postColumn.sortOrder ? 1 : -1);
    }

    function filterableRecucer(columns: DataGridColumn[], gridColumn: DataGridColumn) {
        if (gridColumn.filterable) {
            columns.push(gridColumn);
        }
        return columns;
    }

    function collectionFilterableColumns() {
        const filterableColumns: DataGridColumn[] = [];
        columnContext.value.leftColumns.reduce(filterableRecucer, filterableColumns);
        columnContext.value.primaryColumns.reduce(filterableRecucer, filterableColumns);
        columnContext.value.rightColumns.reduce(filterableRecucer, filterableColumns);
        return filterableColumns;
    }

    function collectionSortableColumns() {
        const sortableColumns: DataGridColumn[] = [];
        columnContext.value.leftColumns.reduce(sortableReducer, sortableColumns);
        columnContext.value.primaryColumns.reduce(sortableReducer, sortableColumns);
        columnContext.value.rightColumns.reduce(sortableReducer, sortableColumns);
        const currentMaxSortOrder = Math.max(...sortableColumns.map((column: DataGridColumn) => column.sortOrder || 0));
        return sortableColumns.sort(decendCompare)
            .map((sortableColumn: DataGridColumn) => {
                const sortOrder = sortableColumn.sortOrder || currentMaxSortOrder + 1;
                // const sortOrder = sortableColumns.length - index;
                sortableColumn.sortOrder = sortOrder;
                return sortableColumn;
            }).sort(sortOrderCompare)
            .map((sortableColumn: DataGridColumn, index: number) => {
                const sortOrder = index + 1;
                sortableColumn.sortOrder = sortOrder;
                return sortableColumn;
            });
    }

    function applySortableColumns(sortableColumns: DataGridColumn[], dataView: UseDataView, sorterComposition: UseSort) {
        const columnSorters = sortableColumns
            .map<DataViewSorter>((sortableColumn: DataGridColumn) => {
                const sorter = sorterComposition.getSorterByColumn(sortableColumn);
                const ascending = sortableColumn.sort === 'asc';
                const decending = sortableColumn.sort === 'desc';
                const sortMethod = ascending ? sorter.ascend : (decending ? sorter.decend : undefined);
                return { field: sortableColumn.field, compare: sortMethod };
            });
        dataView.setSorters(columnSorters);
    }

    function applyColumnSorter(dataView: UseDataView, sorterComposition: UseSort) {
        const sortableColumns: DataGridColumn[] = collectionSortableColumns();
        applySortableColumns(sortableColumns, dataView, sorterComposition);
    }

    return {
        applyColumnSorter, collectionFilterableColumns, applySortableColumns, collectionSortableColumns,
        columnContext, hasLeftFixedColumn, hasRightFixedColumn, updateColumnSettingIcon
    };
}
